package view;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JComponent;

import resources.GlobalSettings;

import model.Level;
import model.pieces.Piece;
import utilities.TransformUtilities;
import view.pieces.PieceDisplayView;

public class LevelDisplayView extends JComponent implements Observer {

	private static final long serialVersionUID = 8924955327244378583L;

	private final Level level;
	private ArrayList<PieceDisplayView> piecesViews;

	private AffineTransform transform;

	public LevelDisplayView() {
		super();
		this.level = null;
		this.piecesViews = new ArrayList<PieceDisplayView>();
		this.transform = new AffineTransform();
	}

	public LevelDisplayView(Level level) {
		super();
		this.level = level;
		this.piecesViews = new ArrayList<PieceDisplayView>();
		this.level.addObserver(this);
		this.transform = new AffineTransform();
	}

	@Override
	public void update(Observable o, Object arg) {
		this.validate();
		this.repaint();
	}

	@Override
	protected void paintComponent(Graphics g) {
		Graphics2D g2D = (Graphics2D) g;
		
		super.paintComponent(g2D);

		if (this.level != null && this.piecesViews.size() == 0) {
			this.createLevelView(g2D);
		}

		if (this.level != null) {
			Rectangle2D levelBounds = getBounds(this.level);
			TransformUtilities.zoomFit(this.transform, levelBounds,
					this.getBounds());
		}
		
		// Apply the transformation
		AffineTransform original = g2D.getTransform( );
		original.concatenate( this.transform );
		g2D.setTransform( original );

		this.paint(g2D);
	}

	private void paint(Graphics2D g2D) {
		for (PieceDisplayView pieceView : this.piecesViews) {
			pieceView.paint(g2D);
		}
	}

	private void createLevelView(Graphics2D g2D) {
		for (Piece piece : this.level.getPieces()) {
			// estrategia de pintado iterativo o centralizado
			piece.addObserver(this);
			
			this.piecesViews.add(PieceViewFactory.createPieceView(g2D, piece));
		}
	}

	private Rectangle2D getBounds(Level level) {
		double minX = Double.POSITIVE_INFINITY, minY = Double.POSITIVE_INFINITY;
		double maxX = Double.NEGATIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY;

		Iterator<Piece> i = level.getPieces().iterator();
		while (i.hasNext()) {
			Piece piece = i.next();
			minX = Math.min(minX, piece.getRow());
			maxX = Math.max(maxX, piece.getRow());
			minY = Math.min(minY, piece.getCol());
			maxY = Math.max(maxY, piece.getCol());
		}

		return new Rectangle2D.Double(minX, minY,
				(maxX - minX + 1) * GlobalSettings.pieceWidth,
				(maxY - minY + 1) * GlobalSettings.pieceHeight);
	}

}
